package com.caocaog.market.service.impl;

import com.caocaog.market.entity.ShopType;
import com.caocaog.market.entity.dto.ShopTypeTree;
import com.caocaog.market.mapper.ShopTypeMapper;
import com.caocaog.market.service.ShopTypeService;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.exception.CheckFailedException;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.base.entity.FieldRedisItem;
import org.jeecg.common.system.base.service.impl.GenericServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Nullable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 店铺分类
 *
 * @author chechen
 * @date 2022-11-03
 */
@Slf4j
@Service
public class ShopTypeServiceImpl extends GenericServiceImpl<ShopTypeMapper, ShopType> implements ShopTypeService {


    @Override
    public List<ShopTypeTree> getShopTypeTree(@Nullable String siteId, @Nullable Boolean isShowFreeze) {
        isShowFreeze = Optional.ofNullable(isShowFreeze).orElse(false);
        List<ShopType> allShopTypeList = getAllShopTypeList();

        return buildShopTypeTree(siteId, isShowFreeze, allShopTypeList);
    }

    /**
     * 构建店铺分类树，共三级
     *
     * @param isShowFreeze 是否展示冻结的节点 为true时展示所有节点
     * @return 店铺分类树
     */
    private static List<ShopTypeTree> buildShopTypeTree(@Nullable String siteId, Boolean isShowFreeze, List<ShopType> allShopTypeList) {
        List<ShopTypeTree> level1 = new ArrayList<>();
        List<ShopTypeTree> level2 = new ArrayList<>();
        List<ShopTypeTree> level3 = new ArrayList<>();

        for (ShopType shopType : allShopTypeList) {
            if ((!shopType.getStatus() && !isShowFreeze)  // 店铺分组为冻结状态，并且不展示冻结的节点，则continue
                    || (siteId != null && shopType.getBlacklist().contains(siteId)) // 节点的黑名单中包含此分站id，则continue
            ) continue;


            switch (shopType.getLevel()) {
                case 1:
                    level1.add(shopType.convertShopTypeTree());
                    break;
                case 2:
                    level2.add(shopType.convertShopTypeTree());
                    break;
                case 3:
                    level3.add(shopType.convertShopTypeTree());
                    break;
                default:
                    throw new JeecgBootException("未知的的店铺分类等级 level-> " + shopType.getLevel());
            }
        }

        Map<Long, ShopTypeTree> level1MapById = level1.stream().collect(Collectors.toMap(ShopTypeTree::getId, shopType -> shopType));
        Map<Long, List<ShopTypeTree>> level2MapByParentId = level2.stream().collect(Collectors.groupingBy(ShopTypeTree::getParentId, Collectors.toList()));

        for (Map.Entry<Long, List<ShopTypeTree>> level2EntryByParentId : level2MapByParentId.entrySet()) {
            Long level1Id = level2EntryByParentId.getKey();
            List<ShopTypeTree> level1ChildrenList = level2EntryByParentId.getValue();
            level1MapById.computeIfPresent(level1Id, (key, value) -> value.addChildren(level1ChildrenList));
        }

        Map<Long, ShopTypeTree> level2MapById = level2.stream().collect(Collectors.toMap(ShopTypeTree::getId, shopType -> shopType));
        Map<Long, List<ShopTypeTree>> level3MapByParentId = level3.stream().collect(Collectors.groupingBy(ShopTypeTree::getParentId, Collectors.toList()));

        for (Map.Entry<Long, List<ShopTypeTree>> level3EntryByParentId : level3MapByParentId.entrySet()) {
            Long level2Id = level3EntryByParentId.getKey();
            List<ShopTypeTree> level2ChildrenList = level3EntryByParentId.getValue();
            level2MapById.computeIfPresent(level2Id, (key, value) -> value.addChildren(level2ChildrenList));
        }

        return level1;
    }


    @Override
    public ShopType getShopTypeById(Long id) {
        return super.getByIdCache(id);
    }


    @Override
    public List<ShopType> getAllShopTypeList() {
        return super.selectListAllCache();
    }


    @Override
    public List<ShopType> findShopTypeListByParentId(Long parentId) {
        return super.selectListByFieldCache(ShopType::getParentId, parentId);
    }


    @Override
    @Transactional
    public int addShopType(ShopType shopType) {
        verification(shopType);

        // 有父级的更新父级店铺分类「是否有子级」字段
        if (shopType.getLevel() > 1) {
            ShopType toBeUpdateParent = getShopTypeById(shopType.getParentId());
            if (!toBeUpdateParent.getHaveChildren()) {
                updateShopType(toBeUpdateParent.setHaveChildren(true));
            }
        }

        return super.insertCache(shopType);
    }


    @Override
    public int updateShopType(ShopType shopType) {
        return super.updateCache(verification(shopType));
    }


    @Override
    @Transactional
    public int deleteShopType(Long... ids) {
        Assert.isTrue(ids != null && ids.length > 0, "ids must not be empty!");

        Map<Long, ShopType> toBeDeleteMap = new HashMap<>();
        List<ShopType> shopTypeList = super.selectListByIdsCache(ids);

        selectShopTypeChildrenTreeList(toBeDeleteMap, shopTypeList);

        Collection<ShopType> toBeDeleteCollection = toBeDeleteMap.values();
        int deleteRecordCount = super.deleteByEntityListAllCache(toBeDeleteCollection);


        int updateRecordCount = 0;
        Long[] parentIds = shopTypeList.stream()
                .map(ShopType::getParentId)
                .distinct()
                .filter(parentId -> parentId != 0)
                .toArray(Long[]::new);
        if (parentIds.length > 0) {
            List<ShopType> toBeUpdateList = super.selectListByIdsCache(parentIds).stream()
                    // 过滤父级还有子节点的shopType
                    .filter(shopType -> findShopTypeListByParentId(shopType.setHaveChildren(false).getId()).isEmpty())
                    .collect(Collectors.toList());
            for (ShopType shopType : toBeUpdateList) shopType.setHaveChildren(false);

            log.info("批量更新父级节点「是否有子级」字段为没有");
            updateRecordCount = super.updateBatchCache(toBeUpdateList);
        }


        return deleteRecordCount + updateRecordCount;
    }


    /**
     * 查询店铺分类集合下的所有分支节点
     *
     * @param map          存储所有递归的对象 < id, ShopType >
     * @param shopTypeList 要查询的店铺分类集合
     */
    private void selectShopTypeChildrenTreeList(Map<Long, ShopType> map, List<ShopType> shopTypeList) {

        for (ShopType shopType : shopTypeList) {
            // map中包含此id，则跳过
            if (map.containsKey(shopType.getId())) continue;
            map.put(shopType.getId(), shopType);

            // 有子集，则查出来递归
            if (shopType.getHaveChildren()) {
                List<ShopType> shopTypeListByParentId = findShopTypeListByParentId(shopType.getId());
                selectShopTypeChildrenTreeList(map, shopTypeListByParentId);
            }
        }

    }


    @Override
    public int changeShopTypeStatus(Long id) {
        ShopType shopType = Optional.ofNullable(getShopTypeById(id))
                .orElseThrow(() -> new CheckFailedException("未找到相应的店铺分类, id-> " + id));

        return updateShopType(shopType.setStatus(!shopType.getStatus()));
    }


    @Override
    public int addBlacklistInShopType(Long id, String... siteIds) {
        ShopType shopType = Optional.ofNullable(getShopTypeById(id))
                .orElseThrow(() -> new CheckFailedException("未找到相应的店铺分类, id-> " + id));

        return updateShopType(shopType.setBlacklist(Arrays.asList(siteIds)));
    }


    /**
     * insert 或 update 前对实体进行处理
     *
     * @param shopType 店铺分类
     * @return 处理后的实体
     */
    private ShopType verification(ShopType shopType) {

        if (shopType.getParentId() == null || shopType.getParentId() == 0L) {
            shopType.setLevel(1);
        } else {
            // 查询 parentId
            ShopType shopTypeParent = Optional.ofNullable(getShopTypeById(shopType.getParentId())).orElseThrow(() ->
                    new CheckFailedException("没有找到相应的父类店铺分类节点, parentId-> " + shopType.getParentId()));
            // 父级的level不能为3
            Assert.isTrue(shopTypeParent.getLevel() != 3, "三级节点无法创建子集");
            // 子集的level = 父级level + 1
            shopType.setLevel(shopTypeParent.getLevel() + 1);
        }

        return shopType;
    }


    @Override
    public String getName() {
        return "店铺分类";
    }

    @Override
    public List<FieldRedisItem<ShopType>> getAllFieldCacheList() {
        return Arrays.asList(
                FieldRedisItem.build(CacheConstant.SHOP_TYPE_ALL),
                FieldRedisItem.build(ShopType::getId, CacheConstant.SHOP_TYPE_INFO, true),
                FieldRedisItem.build(ShopType::getParentId, CacheConstant.SHOP_TYPE_LIST_BY_PARENT_ID)
        );
    }

}
